﻿#define GL_GLEXT_PROTOTYPES
#include <GL/glew.h>
#include <GL/GL.h>
#include "openglscene.h"
#include <QtGui>
#include <QtWidgets>
#include <QtConcurrent/QtConcurrent>


#ifndef GL_MULTISAMPLE
#define GL_MULTISAMPLE  0x809D
#endif

//#define TEST_2
//#define VectorOutput(m, v) qDebug() << QString(QString(m) + "x(%1), y(%2), z(%3)").arg(v.x()).arg(v.y()).arg(v.z());

const float AXIS_SIZE = 15.f;
const float GRID_STEP = 10.f;
const float GRID_SIZE = 100.f;

const float CAMERA_DISTANCE = 16.0f;
const float DEG2RAD         = 3.141593f / 180;

//static Model *loadModel(const QString &filePath)
//{
// //   return new Model(filePath);
//}

//========================================================
OpenGLScene::OpenGLScene()
    : PrinerTimer(this)
      , m_wireframeEnabled(false)
      , m_normalsEnabled(false)
      , m_gcodeMotionEnabled(true)
      , m_modelColor(153, 255, 0)
      , m_backgroundColor(233, 240, 250)
	  ,is_priner_timer_start(false)
	  ,tubesegments(-1)
//    , m_model(0)
//    , m_distance(1.4f)
{
    g_code.open("bunny.gcode");
    g_code.fullydraw = false;
#ifdef QUATERNION_CAMERA
    m_distExp = 6000.f;
    m_trackBall = new TrackBall(0.0f, QVector3D(0,1,0));
#else
    m_cameraAngleX = m_cameraAngleY = 0;
    m_cameraDistance = CAMERA_DISTANCE;
#endif
    // ================ Controls dialog ================
    QWidget *controls = createDialog(tr("Controls"));
    
    m_modelButton = new QPushButton(tr("开始打印"));
    connect(m_modelButton, SIGNAL(clicked()), this, SLOT(setTimer()));
    connect(&PrinerTimer, SIGNAL(timeout()) , this, SLOT(printAline()));
#ifndef QT_NO_CONCURRENT
  //  connect(&m_modelLoader, SIGNAL(finished()), this, SLOT(modelLoaded()));
#endif
    controls->layout()->addWidget(m_modelButton);

    QCheckBox *wireframe = new QCheckBox(tr("显示线框"));
    connect(wireframe, SIGNAL(toggled(bool)), this, SLOT(enableWireframe(bool)));
    controls->layout()->addWidget(wireframe);

    QCheckBox *normals = new QCheckBox(tr("显示法线"));
    connect(normals, SIGNAL(toggled(bool)), this, SLOT(enableNormals(bool)));
    controls->layout()->addWidget(normals);

    QCheckBox *gcodeMotion = new QCheckBox(tr("显示坐标轴"));
    gcodeMotion->setChecked(true);
    connect(gcodeMotion, SIGNAL(toggled(bool)), this, SLOT(enableGCodeMotion(bool)));
    controls->layout()->addWidget(gcodeMotion);

    QCheckBox *linesOnly = new QCheckBox(tr("轨迹"));
    linesOnly->setChecked(true);
    connect(linesOnly, SIGNAL(toggled(bool)), this, SLOT(enableGCodeLines(bool)));
    controls->layout()->addWidget(linesOnly);

    QPushButton *colorButton = new QPushButton(tr("重置打印"));
    connect(colorButton, SIGNAL(clicked()), this, SLOT(setModelColor()));
    controls->layout()->addWidget(colorButton);

    QPushButton *backgroundButton = new QPushButton(tr("选择背景颜色"));
    connect(backgroundButton, SIGNAL(clicked()), this, SLOT(setBackgroundColor()));
    controls->layout()->addWidget(backgroundButton);



    QWidget *statistics = createDialog(tr("Model info"));
    statistics->layout()->contentsMargins().setTop(20);

    for (int i = 0; i < 4; ++i) {
        m_labels[i] = new QLabel;
        statistics->layout()->addWidget(m_labels[i]);
    }

    QGroupBox *posGroupBox = new QGroupBox(tr("Translate"));
    QVBoxLayout *posVBox = new QVBoxLayout();
    QHBoxLayout *posXBox = createSpinBox(tr(" X "), -300, 300, SLOT(translateX(int)));
    QHBoxLayout *posYBox = createSpinBox(tr(" Y "), -300, 300, SLOT(translateY(int)));
    QHBoxLayout *posZBox = createSpinBox(tr(" Z "), -300, 300, SLOT(translateZ(int)));
    posVBox->addLayout(posXBox);
    posVBox->addLayout(posYBox);
    posVBox->addLayout(posZBox);
    posGroupBox->setLayout(posVBox);

    QGroupBox *rotGroupBox = new QGroupBox(tr("Rotate"));
    QVBoxLayout *rotVBox = new QVBoxLayout();
    QHBoxLayout *pitchBox = createSpinBox(tr(" Pitch "), -359, 359, SLOT(rotateX(int)));
    QHBoxLayout *yawBox   = createSpinBox(tr(" Yaw ")  , -359, 359, SLOT(rotateY(int)));
    QHBoxLayout *rollBox  = createSpinBox(tr(" Roll ") , -359, 359, SLOT(rotateZ(int)));
    rotVBox->addLayout(pitchBox);
    rotVBox->addLayout(yawBox);
    rotVBox->addLayout(rollBox);
    rotGroupBox->setLayout(rotVBox);

    QGroupBox *scaleGroupBox = new QGroupBox(tr("Scale"));
    QVBoxLayout *scaleVBox = new QVBoxLayout();
    QHBoxLayout *scaleBox = createDoubleSpinBox(tr(" Ratio "), 0.0, 5.0, 0.01, SLOT(scale(double)));
    scaleVBox->addLayout(scaleBox);
    scaleGroupBox->setLayout(scaleVBox);

    statistics->layout()->addWidget(posGroupBox);
    statistics->layout()->addWidget(rotGroupBox);
    statistics->layout()->addWidget(scaleGroupBox);


    // ================= Scrollbar Dialog ===================
    QWidget *gcodeSlider = createDialog(tr("G-Code Setp"));
    m_slider = createSlider(0, SLOT(gcodeLayers(int)));
    gcodeSlider->layout()->addWidget(m_slider);
    // ================= Merge dialogs ==================
    QWidget *widgets[] = { controls, statistics, gcodeSlider };

    for (uint i = 0; i < sizeof(widgets) / sizeof(*widgets); ++i) {
        QGraphicsProxyWidget *proxy = new QGraphicsProxyWidget(0, Qt::Dialog);
        proxy->setWidget(widgets[i]);
        addItem(proxy);
    }

    QPointF pos(10, 10);
    foreach (QGraphicsItem *item, items()) {
        item->setFlag(QGraphicsItem::ItemIsMovable);
        item->setCacheMode(QGraphicsItem::DeviceCoordinateCache);

        const QRectF rect = item->boundingRect();
        item->setPos(pos.x() - rect.x(), pos.y() - rect.y());
        pos += QPointF(0, 10 + rect.height());
    }
    //draw a sun ☀


    /*QRadialGradient gradient(40, 40, 40, 40, 40);
    gradient.setColorAt(0.2, Qt::yellow);
    gradient.setColorAt(1, Qt::transparent);

    m_lightItem = new QGraphicsRectItem(0, 0, 80, 80);
    m_lightItem->setPen(Qt::NoPen);
    m_lightItem->setBrush(gradient);
    m_lightItem->setFlag(QGraphicsItem::ItemIsMovable);
    m_lightItem->setPos(800, 200);
    addItem(m_lightItem);*/
//    initGL();
   // loadModel(QLatin1String("cube.obj"));

}

OpenGLScene::~OpenGLScene()
{
//    glDeleteBuffersARB(1, &vboId);
}

QDialog *OpenGLScene::createDialog(const QString &windowTitle) const
{
    QDialog *dialog = new QDialog(0, Qt::CustomizeWindowHint | Qt::WindowTitleHint);

    dialog->setWindowOpacity(0.8);
    dialog->setWindowTitle(windowTitle);
    dialog->setLayout(new QVBoxLayout);

    return dialog;
}


QSlider *OpenGLScene::createSlider(int rangeMax, const char *setterSlot)
{
    QSlider *slider = new QSlider(Qt::Horizontal);
    slider->setRange(0, rangeMax);
    slider->setSingleStep(1);
    slider->setPageStep(1);
    slider->setTickInterval(1);
    slider->setTickPosition(QSlider::TicksRight);
    connect(slider, SIGNAL(valueChanged(int)), this, setterSlot);
    return slider;
}

QHBoxLayout* OpenGLScene::createSpinBox(QString label,int rangeFrom, int rangeTo, const char *member) const
{
    QLabel * spinLabel = new QLabel(label);

    QSpinBox *spinBox = new QSpinBox();
    spinBox->setRange(rangeFrom, rangeTo);
    connect(spinBox, SIGNAL(valueChanged(int)), this, member);

    QHBoxLayout * layout = new QHBoxLayout;
    layout->addWidget(spinLabel);
    layout->addWidget(spinBox);

    return layout;
}

QHBoxLayout* OpenGLScene::createDoubleSpinBox(QString label, double rangeFrom, double rangeTo, double singleStep, const char *member) const
{
    QLabel * spinLabel = new QLabel(label);

    QDoubleSpinBox *spinBox = new QDoubleSpinBox();
    spinBox->setRange(rangeFrom, rangeTo);
    spinBox->setSingleStep(singleStep);
    spinBox->setValue(1.0f);
    connect(spinBox, SIGNAL(valueChanged(double)), this, member);

    QHBoxLayout * layout = new QHBoxLayout;
    layout->addWidget(spinLabel);
    layout->addWidget(spinBox);

    return layout;
}

void OpenGLScene::printAline()
{
    g_code.setDrawSegments(++tubesegments);
    tubesegments += 39;
    
}
void OpenGLScene::drawGrid()
{
    // disable lighting
    //    glDisable(GL_DEPTH_TEST);
        // ===== draw transparent plane =====
    glBegin(GL_QUADS);
    glColor4f(0.6f, 0.7, 1.f, 0.5f);

    glVertex3f(GRID_SIZE, 0, GRID_SIZE);
    glVertex3f(-GRID_SIZE, 0, GRID_SIZE);
    glVertex3f(-GRID_SIZE, 0, -GRID_SIZE);
    glVertex3f(GRID_SIZE, 0, -GRID_SIZE);
    glEnd();


    // ===== draw 20x20 grid =====
    glBegin(GL_LINES);

    glColor3f(0.5f, 0.5f, 0.5f);
    for (float i = GRID_STEP; i <= GRID_SIZE; i += GRID_STEP)
    {
        glVertex3f(-GRID_SIZE, 0, i);   // lines parallel to X-axis
        glVertex3f(GRID_SIZE, 0, i);
        glVertex3f(-GRID_SIZE, 0, -i);   // lines parallel to X-axis
        glVertex3f(GRID_SIZE, 0, -i);

        glVertex3f(i, 0, -GRID_SIZE);   // lines parallel to Z-axis
        glVertex3f(i, 0, GRID_SIZE);
        glVertex3f(-i, 0, -GRID_SIZE);   // lines parallel to Z-axis
        glVertex3f(-i, 0, GRID_SIZE);
    }

    // x-axis
    glLineWidth(3);
    glColor3f(1, 0, 0);
    glVertex3f(-GRID_SIZE, 0, 0);
    glVertex3f(GRID_SIZE, 0, 0);

    // z-axis
    glColor3f(0, 0, 1);
    glVertex3f(0, 0, -GRID_SIZE);
    glVertex3f(0, 0, GRID_SIZE);

    glEnd();

    // enable lighting back
    //    glEnable(GL_DEPTH_TEST);
}
inline void OpenGLScene::initGL()
{
    glShadeModel(GL_SMOOTH);
//    glPixelStorei(GL_UNPACK_ALIGNMENT, 4);      // 读取image 对其方式默认4  rgba4 通道读取 如果读取jpg 3通道图 则会因缺少a通道出错

    // enable /disable features
    glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
    glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);
    glHint(GL_POLYGON_SMOOTH_HINT, GL_NICEST);

    // enable /disable features
    glEnable(GL_DEPTH_TEST);
	/*glEnable(GL_CULL_FACE);
    glCullFace(GL_BACK);*/
    glEnable(GL_BLEND);
    glEnable(GL_LIGHTING);
   
    glEnable(GL_NORMALIZE);

    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    glEnable(GL_LINE_SMOOTH);
    glEnable(GL_LEQUAL);

    // track material ambient and diffuse from surface color, call it before glEnable(GL_COLOR_MATERIAL)
    glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE);
    glEnable(GL_COLOR_MATERIAL);
    
}

void OpenGLScene::initLights()
{

}



void OpenGLScene::drawBackground(QPainter *painter, const QRectF &)
{
    //循环执行draw 每一帧执行
    //判断是否绑定到viewport
    if (painter->paintEngine()->type() != QPaintEngine::OpenGL
     && painter->paintEngine()->type() != QPaintEngine::OpenGL2)
    {
        qWarning("OpenGLScene: drawBackground needs a QGLWidget to be set as viewport on the graphics view");
        return;
    }
    


	painter->beginNativePainting();
    initGL();
    glClearColor(m_backgroundColor.redF(), m_backgroundColor.greenF(), m_backgroundColor.blueF(), 1.0f);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
    glMatrixMode(GL_PROJECTION); //switch to projection matrix
    glPushMatrix();              //save current projection matrix
    glLoadIdentity();            //reset projection matrix
    gluPerspective(60, width() / height(), 0.01, 1000);
    glMatrixMode(GL_MODELVIEW);
    glPushMatrix();
    glLoadIdentity();
	QMatrix4x4 view;
    view *= transformmat;
    view.rotate(m_trackBall->rotation());
    view(2, 3) -= 2.0f * exp(m_distExp / 1200.0f);
    view.translate({ 0,-60,0 });
    glLoadMatrixf(view.constData());
	//g_code.draw(false, false);
    drawGrid();
    g_code.drawTube();
	glPopMatrix();
    glMatrixMode(GL_PROJECTION);
    glPopMatrix();
	painter->endNativePainting();
	QTimer::singleShot(20, this, SLOT(update()));
}

void OpenGLScene::setModelColor()
{
	if (!is_priner_timer_start)
	{
        tubesegments = -1;
	}
    printAline();
}


void OpenGLScene::enableWireframe(bool enabled)
{
    m_wireframeEnabled = enabled;
    update();
}

void OpenGLScene::enableNormals(bool enabled)
{
    m_normalsEnabled = enabled;
    update();
}


void OpenGLScene::enableGCodeMotion(bool enabled)
{
    m_gcodeMotionEnabled = enabled;
    update();
}

void OpenGLScene::enableGCodeLines(bool enabled)
{
    m_gcodeLinesEnabled = enabled;
    update();
}


void OpenGLScene::setBackgroundColor()
{
    const QColor color = QColorDialog::getColor(m_backgroundColor);
    if (color.isValid()) {
        m_backgroundColor = color;
        update();
    }
}

void OpenGLScene::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
    QGraphicsScene::mousePressEvent(event);
    if (event->isAccepted())
        return;

    if (event->buttons() & Qt::LeftButton) {
#ifdef QUATERNION_CAMERA
        m_trackBall->push(pixelPosToViewPos(event->scenePos()), QQuaternion());
#endif
    }
    event->accept();
}

void OpenGLScene::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{
    QGraphicsScene::mouseMoveEvent(event);
    if (event->isAccepted())
        return;

    if (event->buttons() & Qt::LeftButton) {
#ifdef QUATERNION_CAMERA
        m_trackBall->move(pixelPosToViewPos(event->scenePos()), QQuaternion());
#else
    
#endif
        event->accept();
        update();
    } else {
        m_trackBall->release(pixelPosToViewPos(event->scenePos()), QQuaternion());
    }
}

void OpenGLScene::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
{
    QGraphicsScene::mouseReleaseEvent(event);
    if (event->isAccepted())
        return;

#ifdef QUATERNION_CAMERA
    m_trackBall->release(pixelPosToViewPos(event->scenePos()), QQuaternion());
#endif

    event->accept();
    update();
}

void OpenGLScene::wheelEvent(QGraphicsSceneWheelEvent *event)
{
    QGraphicsScene::wheelEvent(event);
    if (event->isAccepted())
        return;

#ifdef QUATERNION_CAMERA
    m_distExp += event->delta();
    if (m_distExp < -8 * 120)
        m_distExp = -8 * 120;
    if (m_distExp > 10 * 700)
        m_distExp = 10 * 700;
#else
#endif
    event->accept();
    update();
}


QPointF OpenGLScene::pixelPosToViewPos(const QPointF& p) const
{
    return {
	    2.0 * static_cast<float>(p.x()) / width() - 1.0,
                   1.0 - 2.0 * static_cast<float>(p.y()) / height()
    };
}


void OpenGLScene::translateX(int value)
{
    qDebug() << Q_FUNC_INFO << value;
   
    transformmat.translate(value, 0, 0);
//    m_model->transform(m);
    update();
}

void OpenGLScene::translateY(int value)
{
    qDebug() << Q_FUNC_INFO << value;

    transformmat.translate(0, value, 0);
//    m_model->transform(m);
    update();
}

void OpenGLScene::translateZ(int value)
{
    qDebug() << Q_FUNC_INFO << value;

    transformmat.translate(0, 0, value);
//    m_model->transform(m);
    update();
}

void OpenGLScene::rotateX(int value)
{
    qDebug() << Q_FUNC_INFO << value;

    transformmat.rotate(value, 1, 0, 0);
//    m_model->transform(m);
    update();
}

void OpenGLScene::rotateY(int value)
{
    qDebug() << Q_FUNC_INFO << value;

    transformmat.rotate(value, 0, 1, 0);
//    m_model->transform(m);
    update();
}

void OpenGLScene::rotateZ(int value)
{
    qDebug() << Q_FUNC_INFO << value;
   
    transformmat.rotate(value, 0, 0, 1);
//    m_model->transform(m);
    update();
}


void OpenGLScene::scale(double value)
{
    qDebug() << Q_FUNC_INFO << value;
   
    transformmat.scale(value, value, value);
  //  m_model->transform(m);
    update();
}


void OpenGLScene::gcodeLayers(int layers)
{
//    qDebug() << Q_FUNC_INFO << layers;
//    m_model->setGCodeLayers(layers);
    update();
}

void OpenGLScene::setTimer()
{
	if (!is_priner_timer_start)
	{
        dynamic_cast<QPushButton*>(m_modelButton)->setText("停止打印");
        PrinerTimer.start(20);
	}
	else
	{
        dynamic_cast<QPushButton*>(m_modelButton)->setText("开始打印");
        PrinerTimer.stop();
	}
    is_priner_timer_start = !is_priner_timer_start;
}
